package com.wzw.springboot.principle.mvc.config;

import com.wzw.springboot.principle.aware.CustomApplicationContext;
import com.wzw.springboot.principle.mvc.argumentresolver.CustomizedHandlerMethodArgumentResolver;
import com.wzw.springboot.principle.mvc.argumentresolver.CustomizedHandlerMethodReturnValueHandler;
import com.wzw.springboot.principle.mvc.argumentresolver.SecurityHandlerMethodArgumentResolver;
import com.wzw.springboot.principle.mvc.converter.CustomizedHttpMessageConverter;
import com.wzw.springboot.principle.mvc.interceptors.CustomizedInterceptorOne;
import com.wzw.springboot.principle.mvc.interceptors.CustomizedInterceptorTwo;
import com.wzw.springboot.principle.mvc.interceptors.CustomizedWebRequestInterceptor;
import com.wzw.springboot.principle.mvc.interceptors.SecurityUserInterceptor;
import com.wzw.springboot.principle.use.security.manage.TokenManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * WebMvc配置：拦截器、视图解析器、消息转换器等。
 *
 * 自动装配机制：WebMvcAutoConfiguration{@link org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration}
 *            启动时自动加载，注入：WebMvcAutoConfigurationAdapter、EnableWebMvcConfiguration 配置对象。
 *            有一个重要的自动注入条件限制：@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
 *            也是是说有 WebMvcConfigurationSupport bean对象时就不会生效了，即使用@EnableWebMvc注解，代表用户全面接管配置。
 *
 * WebMvcConfigurer：webMvc顶层配置扩展类，内部包含常用的拦截对象注入。如：拦截器、方法参数解析器、advice对象等的注入。一般客制化的配置可以通过这个实现
 * 默认有两个实现：
 *      1.WebMvcAutoConfigurationAdapter{@link org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter}
 *        是WebMvcAutoConfiguration的内部类，启动时被加载。采用适配器模式，用于适配WebMvcConfigurer。
 *        自动注入的较重要的配置组件：a: 消息转换器 HttpMessageConverters
 *                               b: 路径匹配规则 PathMatchConfigurer
 *                               c: 内部资源的试图解析器 InternalResourceViewResolver
 *                               d: bean名称解析器 BeanNameViewResolver
 *
 *      2.WebMvcConfigurerComposite{@link org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite}
 *        web配置的聚合类，采用聚合模式。实现了顶层接口，并在内部使用 List 存储多个WebMvcAutoConfiguration配置类。
 *
 * WebMvcConfigurationSupport: 也是webMvc配置类，采用模板方法的模式。内部注入了很多重要的组件，如：HandlerMapping（处理器映射器）、RequestMappingHandlerAdapter（处理器适配器）
 *                             在SpringBoot配置中，该类的Bean对象只能存在一个。
 * 默认的较重要实现类：
 *      1: DelegatingWebMvcConfiguration: 此类内部聚合了WebMvcConfigurerComposite，默认在自动装配时创建。内部有自动注入多个WebMvcConfigurer对象，然后set到WebMvcConfigurerComposite中
 *      2: EnableWebMvcConfiguration : 此类是WebMvcAutoConfiguration的内部类，继承了DelegatingWebMvcConfiguration类。
 *
 * 简而言之：webMvc所有的默认配置和用户自定义的配置最后都会被加载到 WebMvcConfigurationSupport 内部。
 *
 * JSON数据格式传输时的通用拦截扩展方法
 * 随者消息转换器初始化时被创建
 * RequestBodyAdvice: 请求的拦截器，可以对请求内容做通用的配置
 * ResponseBodyAdvice: 请求结果响应时的拦截器，可以对请求响应结果做通用的配置
 * RequestResponseBodyAdviceChain: 上述两种拦截器的执行链对象，内部聚合了List<Advice>
 *
 * 请求参数解析过程：
 *  1.执行HandlerAdapter.handle()方法
 *  2.默认实现类：RequestMappingHandlerAdapter.handle()
 *  3.默认实现类：RequestMappingHandlerAdapter.invokeHandlerMethod()
 *  4.ServletInvocableHandlerMethod.invokeAndHandle()
 *  4.ServletInvocableHandlerMethod.invokeForRequest()
 *  4.ServletInvocableHandlerMethod.getMethodArgumentValues() 这一步就是把JSON解析成对于的Class对象，是一个For循环。也就是说依次解析方法上的每一个参数类型
 *  5.HandlerMethodArgumentResolverComposite.resolveArgument() 这个对象时参数解析器的聚合类，内部包了一个List存储所有解析器。
 *  5.HandlerMethodArgumentResolverComposite.getArgumentResolver() 找到一个可以支持解析的方法参数解析器 默认的JSON解析器内部调用了Advice相关接口（不是每一个解析器都会去调用Advice接口）
 *    -- 只有@RequestBody 和 @ResponseBody 才会被RequestResponseBodyMethodProcessor执行器。它本质上是实现了参数解析和返回值解析的接口，其内部回去调用HTTP转换器去读写数据。
 *    -- 其他大部分都是在参数解析这个过程，就完成了对象的映射。不会调用到Advice与Http消息转换器
 *  5.RequestResponseBodyMethodProcessor.resolveArgument() JSON格式的参数解析器
 *  5.RequestResponseBodyMethodProcessor.readWithMessageConverters() 通过消息转换器读取请求内容
 *    1.ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest); 创建一个HttpInputMessage，用于传递
 *    2.readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) 跳到父类的方法
 *      1.遍历执行消息转换器的canRead方法，找到一个可以执行此类型数的转换器
 *      2.调用执行链RequestResponseBodyAdviceChain.beforeBodyRead()方法返回request（已被消息转换器读取完成了），上面也说了其内部包含了很对参数拦截器。这里就是循环去执行所有RequestBodyAdvice.beforeBodyRead()方法
 *      2.调用执行链RequestResponseBodyAdviceChain.afterBodyRead()方法返回request（对读好的请求内容在做拦截），循环去执行所有RequestBodyAdvice.beforeBodyRead()方法
 *  6.在通过反射执行controller里对应的方法
 *  7.执行RequestResponseBodyAdviceChain的返回结果前置拦截和后置拦截，也就是执行所有ResponseAdvice里的方法
 *  8.返回结果
 *
 *
 *
 *
 *
 * @author Wangzhiwen
 */
@Configuration
public class CustomizedWebMvcConfig implements WebMvcConfigurer {

    /**
     * 添加格式化组件
     * @param registry FormatterRegistry
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        WebMvcConfigurer.super.addFormatters(registry);
    }

    /**
     * 系统资源访问
     * @param registry ResourceHandlerRegistry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("").addResourceLocations("");
    }

    /**
     * 添加拦截器
     * @param registry InterceptorRegistry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addWebRequestInterceptor(new CustomizedWebRequestInterceptor()).addPathPatterns("/**");
        registry.addInterceptor(new CustomizedInterceptorOne()).addPathPatterns("/**");
        registry.addInterceptor(new CustomizedInterceptorTwo()).addPathPatterns("/**");
        registry.addInterceptor(new SecurityUserInterceptor(CustomApplicationContext.getBean(TokenManager.class))).addPathPatterns("/**");
    }

    /**
     * 添加参数解析器
     * @param resolvers HandlerMethodArgumentResolver
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new CustomizedHandlerMethodArgumentResolver());
        resolvers.add(new SecurityHandlerMethodArgumentResolver());
    }

    /**
     * 返回值解析器
     * @param handlers HandlerMethodReturnValueHandler
     */
    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        handlers.add(new CustomizedHandlerMethodReturnValueHandler());
    }

    /**
     * TODO: 请求体消息转换器，还没遇到要添加的场景
     * @param converters HttpMessageConverter
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new CustomizedHttpMessageConverter());
    }


}
